#include "netdec.hpp"

const static bool DEBUG = false;

Netdec::Netdec ()
  : head (DEC_NULL), tCount (0), tSize (0),
    dCount (0), dSize (0), realBps (0), realPps (0), pktBps (0), pktPps (0),
    errmsg ("")
{
  memset (this->dec, 0, sizeof (this->dec));
  memset (this->tval, 0, sizeof (this->tval));

  /*************************************************************
   * 2 of 2. Add a line of creating a deocder 
   *************************************************************/
  this->dec [DEC_ETHER] = new EtherDecoder ();
  this->dec [DEC_IPV4]  = new IPv4Decoder ();
  this->dec [DEC_IPV6]  = new IPv6Decoder ();
  this->dec [DEC_ICMP4] = new Icmp4Decoder ();
  this->dec [DEC_ICMP6] = new Icmp6Decoder ();
  this->dec [DEC_TCP]   = new TcpDecoder ();
  this->dec [DEC_UDP]   = new UdpDecoder ();

  for (int i = 0; i < DECODER_COUNT; i++)
    {
      if (this->dec [i])
	this->dec [i]->timer = &(this->timer);
    }    
}

Netdec::~Netdec ()
{
  for (int i = 0; i < DECODER_COUNT; i++)
    {
      if (this->dec [i])
	delete this->dec [i];
    }
};


bool Netdec::setup ()
{
  switch (this->iport.getLinkType ())
    {
    case InputPort::LT_ETHERNET: this->head = DEC_ETHER; break;
    case InputPort::LT_RAWIP:    this->head = DEC_IPV4;  break;
    default: 
      return false;
    }

  return true;
}

bool Netdec::setupDevice (string devName, string filter)
{
  if (! this->iport.openInterface (devName) ||
      (filter != "" && ! this->iport.setFilter (filter)))
    {
      this->setErrMsg (this->iport.getErrMsg ());
      return false;
    }

  if (! this->setup ())
    return false;

  return true;
}

bool Netdec::setupFile (string fileName, string filter)
{
  if (! this->iport.openFile (fileName) ||
      (filter != "" && ! this->iport.setFilter (filter)))
    {
      this->setErrMsg (this->iport.getErrMsg ());
      return false;
    }

  if (! this->setup ())
    return false;

  return true;
}


NDecResult * Netdec::fetch (void)
{
#define DEBUG_FETCH false
  DecType next = this->head;
  Record * rec = NULL;

  debug (DEBUG_FETCH, "Enter capturing");

  if (! this->iport.getPacket (&this->pkt))
    return NULL;

  this->timer.proc (this->pkt.getTimeval ());

  debug (DEBUG_FETCH, "Enter decoding");

  this->res.release ();
  this->res.setPacket (this->pkt);
  this->res.setDecodeTimer (this->timer);

  while (next != DEC_NULL && this->dec[next] && 
	 NULL != (rec = this->dec[next]->decode (*(this->res.getPacket ()), *rec)))
    {
      this->res.push (rec);
      next = rec->getNextDecoder ();
    }

  recordPerformance ();
  return  &(this->res);
}


NDecFilter * Netdec::makeFilter (string name, string arg)
{
  NDecFilter * f = NULL;

  for (int i = 0; i < DECODER_COUNT; i++)
    {      
      if (this->dec [i] && (NULL != (f = this->dec [i]->makeFilter (name))))
	break;
    }

  if (f == NULL)
    {
      this->setErrMsg ("No such name network filter '" + name + "'");
      return NULL;
    }
  
  if (arg != "" && ! f->setup (arg))
    {
      this->setErrMsg (f->getErrMsg ());
      delete f;
      return NULL;
    }

  return f;
}


NDecMpxFilter * Netdec::makeMpxFilter (string name)
{
  NDecMpxFilter * f = NULL;

  for (int i = 0; i < DECODER_COUNT; i++)
    {
      if (this->dec [i] && (NULL != (f = this->dec [i]->makeMpxFilter (name))))
	break;
    }

  return f;
}


void Netdec::recordPerformance ()
{
  if (this->tval[REAL_BEGIN].tv_sec == 0)
    {
      gettimeofday (&(this->tval[REAL_BEGIN]), NULL);
      memcpy (&(this->tval[REAL_CHECK]), &(this->tval[REAL_BEGIN]),
	      sizeof (struct timeval));
      memcpy (&(this->tval[REAL_NOW]), &(this->tval[REAL_BEGIN]),
	      sizeof (struct timeval));
      memcpy (&(this->tval[PKT_BEGIN]), pkt.getTimeval (), 
	      sizeof (struct timeval));
      memcpy (&(this->tval[PKT_CHECK]), pkt.getTimeval (), 
	      sizeof (struct timeval));
    }

  gettimeofday (&(this->tval[REAL_NOW]), NULL);  

  this->tCount++;
  this->dCount++;
  this->tSize += this->pkt.getCapLen ();
  this->dSize += this->pkt.getCapLen ();
}

void Netdec::beat ()
{
  struct timeval rSub, pSub;
  
  timersub (&this->tval[REAL_NOW], &this->tval[REAL_CHECK], &rSub);
  timersub (&this->tval[PKT_NOW],  &this->tval[PKT_CHECK],  &pSub);
  double rSpan = ((double)rSub.tv_usec) / 1000000 + (double) rSub.tv_sec;
  double pSpan = ((double)pSub.tv_usec) / 1000000 + (double) pSub.tv_sec;
  
  this->realSpan = rSpan;
  this->realBps  = ((double)this->dSize * 8) / rSpan;
  this->realPps  = ((double)this->dCount)    / rSpan;
  this->pktSpan  = pSpan;
  this->pktBps   = ((double)this->dSize * 8) / pSpan;
  this->pktPps   = ((double)this->dCount)    / pSpan;

  this->dSize = 0;
  this->dCount = 0;
  memcpy (&(this->tval[REAL_CHECK]), &(this->tval[REAL_NOW]),
	  sizeof (struct timeval));
  memcpy (&(this->tval[PKT_CHECK]), &(this->tval[PKT_NOW]),
	  sizeof (struct timeval));
}


double Netdec::getDeltaSpan ()
{
  return this->realSpan;
}


double Netdec::getDeltaPPS ()
{
  return this->realPps;
}

double Netdec::getDeltaBPS ()
{
  return this->realBps;
}

double Netdec::getTotalSpan ()
{
  struct timeval sub;
  timersub (&this->tval[REAL_NOW], &this->tval[REAL_BEGIN], &sub);
  double span = ((double)sub.tv_usec) / 1000000 + (double) sub.tv_sec;
  return span;
}
  
double Netdec::getTotalPPS ()
{
  double span = this->getTotalSpan ();
  return ((double)this->tCount) / span;
}

double Netdec::getTotalBPS ()
{
  double span = this->getTotalSpan ();
  return ((double)this->tSize * 8) / span;
}


  
